package com.mybatis.crudservice.tools;

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @ 描 述 : mapper工具类
 * @ 作 者 : 一念〃
 * @ 时 间 : 16:50 2019/5/25
 * @ 备 注 :
 */
public class MapperUtils {

    private static DocumentBuilder db;
    private static ResourcePatternResolver rps = new PathMatchingResourcePatternResolver();
    private static Map<String, String> mapperNames = new ConcurrentHashMap();
    private static ConcurrentHashMap<String, Condition> conditionMap = new ConcurrentHashMap();
    private static final Lock lock = new ReentrantLock();
    private static final Condition condition = lock.newCondition();

    static {
        try {
            db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            db.setEntityResolver((publicId,systemId) ->{
                //新建xml
                return new InputSource(new ByteArrayInputStream("<?xml version='1.0' encoding='UTF-8'?>".getBytes()));
            });
        }
        catch (ParserConfigurationException e) {
            e.printStackTrace();
        }
    }

    public static String getMapperName(String mapperName, String selectName){
        if (!mapperNames.containsKey(mapperName)) {
            Condition condition = (Condition)conditionMap.putIfAbsent(mapperName, lock.newCondition());
            if (condition == null) {
                if (!loadMapper(mapperName)) {
                    mapperNames.put(mapperName, "");
                }
                try {
                    lock.lock();
                    ((Condition)conditionMap.remove(mapperName)).signalAll();
                }finally {
                    lock.unlock();
                }
            } else {
                try {
                    lock.lock();
                    if (conditionMap.containsKey(mapperName)) {
                        condition.await(30L, TimeUnit.SECONDS);
                    }
                } catch (Exception e) {

                }finally {
                    lock.unlock();
                }
            }
        }
        String currentMapperName = (String)mapperNames.get(mapperName);
        if ((currentMapperName != null) && (!currentMapperName.equals(""))) {
            return currentMapperName + "." + selectName;
        }
        return null;
    }

    private static boolean loadMapper(String mapperName) {
        Resource[] mapperResources = null;
        try {
            mapperResources = rps.getResources("classpath*:mapper/**/" + mapperName + ".xml");
        } catch (IOException e) {
            e.printStackTrace();
        }
        if ((mapperResources == null) || (mapperResources.length == 0)) {
            return false;
        }
        Resource mapperResource = mapperResources[0];

        String fileName = mapperResource.getFilename();

        String mapperXmlName = fileName.substring(0, fileName.length() - 4);

        Document document = null;
        try {
            InputStream is = mapperResource.getInputStream();
            document = db.parse(new InputSource(is));
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        Element root = document.getDocumentElement();
        String namespace = root.getAttribute("namespace");

        mapperNames.put(mapperXmlName, namespace);

        return true;
    }

}
